package core;

import java.awt.Dimension;
import java.awt.Menu;
import java.awt.MenuBar;
import java.awt.MenuItem;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Random;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

import view.component.ViewPort;
import view.renderer.CursorRenderer;
import view.renderer.EntityRenderer;
import view.renderer.TerrainRenderer;
import core.input.EditModeKeyListener;
import core.input.TestModeKeyListener;
import data.entity.Entity;
import data.entity.monster.Monster;
import data.entity.player.Player;
import data.input.Cursor;
import data.map.TerrainMap;

public class Game implements  ActionListener, KeyListener {

	public static final String GAME_NAME = "Box";
	public static final Dimension RESOLUTION = new Dimension(1024, 768);
	public static final int MAP_SIZE = 32;
	public static final int TILE_SIZE = 32;
	public static final int MODE_EDIT = 0;
	public static final int MODE_TEST = 1;
	
	
	private JFrame frame;
	private ViewPort port;

	private CursorRenderer cursorRenderer;
	private Cursor cursor;

	private TerrainRenderer terrain;
	private TerrainMap map;
	
	private EntityRenderer entityRenderer;
	private Player player;
	private LinkedList<Entity> entities;
	
	
	private EditModeKeyListener editModeKeyListener;
	private TestModeKeyListener testModeKeyListener;
	
	private LinkedList<KeyListener> keyListeners;
	private Thread loop;
	
	  private final Lock lock    = new ReentrantLock();
	
	private int mode;

	public Game() {

	}

	public void load() {
		mode = MODE_EDIT;
		initializeGUI();
		initializeRenderEngine();
		initializeInputEngine();
		startGame();
	}

	private void startGame() {
		frame.setVisible(true);
		loop = new Thread(){
			
			@Override
			public void run(){
				loop();
			}
			public synchronized void loop(){
				while(true){
					try {
						if(mode != MODE_TEST) lock.lock();
						this.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					update();
					SwingUtilities.invokeLater(new Runnable(){

						@Override
						public void run() {
							port.repaint();
						}
						
					});
				}
				
			}
		};
		loop.start();
	}

	private void initializeGUI() {
		
	
		frame = new JFrame(GAME_NAME);
		frame.setSize(RESOLUTION);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		port = new ViewPort();

		MenuBar menu = new MenuBar();
		Menu datei = new Menu("Datei");
		MenuItem neu = new MenuItem("Neu");
		MenuItem save = new MenuItem("Speichern");
		MenuItem load = new MenuItem("Laden");
		MenuItem end = new MenuItem("Beenden");
		Menu mode = new Menu("Modus");
		MenuItem editMode = new MenuItem("Edit");
		MenuItem playMode = new MenuItem("Test");
		mode.add(editMode);
		mode.add(playMode);
		menu.add(datei);
		menu.add(mode);
		datei.add(neu);
		datei.add(save);
		datei.add(load);
		datei.add(end);
		frame.setMenuBar(menu);
		datei.addActionListener(this);
		mode.addActionListener(this);
		frame.add(port);

	}

	private void initializeRenderEngine() {
		map = new TerrainMap(MAP_SIZE);
		terrain = new TerrainRenderer(map, TILE_SIZE);
		port.registerRenderer(terrain);

		cursor = new Cursor();
		cursorRenderer = new CursorRenderer(cursor, TILE_SIZE);
		port.registerRenderer(cursorRenderer);
		
		entities = new LinkedList<Entity>();
//		player = new Player(100,100,32,32);
//		Monster monster = new Monster(500,500,32,32);
//		Monster monster2 = new Monster(500, 100, 32, 32);
//		entities.add(player);
//		entities.add(monster);
//		entities.add(monster2);
		entityRenderer = new EntityRenderer(entities);

		
	}

	
	private void initializeInputEngine(){
		keyListeners = new LinkedList<KeyListener>();
		editModeKeyListener = new EditModeKeyListener(cursor, map);
		testModeKeyListener = new TestModeKeyListener(player);
		frame.addKeyListener(this);
		keyListeners.add(editModeKeyListener);
	}
	
	public void update(){
		Iterator<Entity> it = entities.iterator();
		Random rn = new Random(System.currentTimeMillis());
		while(it.hasNext()){
			Entity e = it.next();
			if(e instanceof Monster){
				
				if(rn.nextBoolean()){
					if(e.getRectangle().x < player.getRectangle().x){
						e.getRectangle().x = e.getRectangle().x + e.getSpeed();
					}
					else e.getRectangle().x = e.getRectangle().x - e.getSpeed();
				}
			else{
				if(e.getRectangle().y < player.getRectangle().y){
					e.getRectangle().y = e.getRectangle().y + e.getSpeed();
				}
				else e.getRectangle().y = e.getRectangle().y - e.getSpeed();
			}
			}
		}
	}



	@Override
	public void actionPerformed(ActionEvent e) {
		String command = e.getActionCommand();

		if (command.equals("Neu")) {
			map.rerollMap();
			port.repaint();
		} else if (command.equals("Speichern")) {
			JFileChooser jf = new JFileChooser();
			int i = jf.showSaveDialog(frame);
			if (i == 0) {
				String str = map.getMapAsString();

				try {
					PrintWriter w = new PrintWriter(jf.getSelectedFile()
							.getPath());
					w.println(str);
					w.close();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
				;
			}
		} else if (command.equals("Laden")) {
			JFileChooser jf = new JFileChooser();
			int i = jf.showOpenDialog(frame);
			if (i == 0) {
				try {
					String str = new BufferedReader(new FileReader(
							jf.getSelectedFile())).readLine();
					map.buildFromString(str);
					port.repaint();
				} catch (FileNotFoundException e1) {
					e1.printStackTrace();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			}

		} else if (command.equals("Beenden")) {
			System.exit(0);
		}
		else if(command.equals("Edit")){
			switchModeTo(MODE_EDIT);
		}
		else if(command.equals("Test")){
			switchModeTo(MODE_TEST);
		}

	}

	private synchronized void switchModeTo(int newMode) {
		if(mode == newMode) return;
		mode = newMode;
		port.resetRenderers();
		keyListeners.clear();
		
		switch(mode){
		case MODE_EDIT: port.registerRenderer(terrain);
		port.registerRenderer(cursorRenderer);
		keyListeners.add(editModeKeyListener);
		break;
		case MODE_TEST: port.registerRenderer(terrain);
		port.registerRenderer(entityRenderer);
		keyListeners.addFirst(testModeKeyListener);
		lock.unlock();
		break;
		}
		
		port.repaint();
	}

	@Override
	public void keyTyped(KeyEvent e) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void keyPressed(KeyEvent e) {
		Iterator<KeyListener> it = keyListeners.iterator();
		while (it.hasNext()){
			it.next().keyPressed(e);
		}
//		if(mode == MODE_TEST) update();
		port.repaint();
		
	}

	@Override
	public void keyReleased(KeyEvent e) {
		// TODO Auto-generated method stub
		
	}

}
